//
//  BLECenter.m
//  BLESDKDemo
//
//  Created by 李阳 on 2018/4/5.
//  Copyright © 2018年 李阳. All rights reserved.
//

#import "BLECenter.h"
#import "BLEDevice.h"
#import "BLEOperation.h"
#import "NSError+BLEAdd.h"
#import "NSArray+BLEAdd.h"
#import "BLERequest.h"
#import "BLESession.h"

/// 创建一个组
static dispatch_group_t ble_central_manager_dispatch_group()
{
    static dispatch_group_t ble_central_manager_dispatch_group;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ble_central_manager_dispatch_group = dispatch_group_create();
    });
    
    return ble_central_manager_dispatch_group;
}

/// 创建一个串行队列
static dispatch_queue_t ble_central_manager_dispatch_queue()
{
    static dispatch_queue_t ble_central_manager_dispatch_queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // Serial是连续 串行的意思
        ble_central_manager_dispatch_queue = dispatch_queue_create("cn.appscomm.central.manager.dispatch", DISPATCH_QUEUE_SERIAL);
    });
    
    return ble_central_manager_dispatch_queue;
}


@implementation BLEUUIDItem

+ (instancetype)itemWithServiceName:(NSString *)serviceName
                         notifyName:(NSString *)notifyName
                          writeName:(NSString *)writeName
{
    return [[self alloc]initWithServiceName:serviceName notifyName:notifyName writeName:writeName];
}

- (instancetype)initWithServiceName:(NSString *)serviceName
                         notifyName:(NSString *)notifyName
                          writeName:(NSString *)writeName
{
    self = [super init];
    if (!self)
    {
        return nil;
    }
    
    _serviceName = serviceName;
    _notifyCharacteristicName = notifyName;
    _writeCharacteristicName = writeName;
    
    return self;
}

- (NSArray<CBUUID *> *)characteristicUUIDs
{
    NSMutableArray *uuids = [NSMutableArray arrayWithCapacity:2];
    if (self.notifyCharacteristicName.length)
    {
        [uuids addObject:self.notifyCharacteristicName];
    }
    if (self.writeCharacteristicName.length)
    {
        [uuids addObject:self.writeCharacteristicName];
    }
    return [uuids ble_map:^id(NSString *obj, NSUInteger idx) {
        return [CBUUID UUIDWithString:obj];
    }];
}

@end

#pragma mark -
@interface BLETask ()

/// 任务状态
@property (nonatomic, assign) BLETaskState state;
/// 错误
@property (nonatomic, strong) NSError *error;
@end

#pragma mark -
@interface BLEDevice ()

///
@property (nonatomic, assign) BOOL retrieveFromCenter;
/// 蓝牙中心
@property (nonatomic, weak) BLECenter *center;

/// 开始监听state
- (void)beginOberverState;
/// 停止监听state
- (void)endOberverState;

@end


#pragma mark -

@implementation BLECenterConfig

+ (instancetype)defaultConfig
{
    return [[[self class] alloc] init];
}

- (instancetype)init
{
    self = [super init];
    if (!self) return nil;
    _connectAutomatically = YES;
    _maxWriteValueLengthForOnce = 20;
    _popAlertWhenPowerOff = YES;
    
    _main = [BLEUUIDItem itemWithServiceName:@"6006" notifyName:@"8002" writeName:@"8001"];
    _NFC = [BLEUUIDItem itemWithServiceName:@"FF10" notifyName:@"FF12" writeName:@"FF11"];;
    _media = [BLEUUIDItem itemWithServiceName:@"7006" notifyName:@"8004" writeName:@"8003"];
    _heart = [BLEUUIDItem itemWithServiceName:@"180D" notifyName:@"2A37" writeName:nil];
    _pay = [BLEUUIDItem itemWithServiceName:@"71E35E48-627B-11E3-BF13-D231FEB1DC81" notifyName:@"027C2675-75FE-4053-9F6F-2955DD3D9AD8" writeName:@"FC72BF5A-DE74-4134-91F2-E2120FEE2BC3"];
    _OTA = [BLEUUIDItem itemWithServiceName:@"1530" notifyName:@"1531" writeName:@"1532"];
    _OTAL42 = [BLEUUIDItem itemWithServiceName:@"00001530-1212-EFDE-1523-785FEABCD123" notifyName:@"00001531-1212-EFDE-1523-785FEABCD123" writeName:@"00001532-1212-EFDE-1523-785FEABCD123"];
    
    return self;
}

- (dispatch_queue_t)completionQueue
{
//    return _completionQueue ?: ble_central_manager_dispatch_queue();
    return _completionQueue ?: dispatch_get_main_queue();
}

- (dispatch_group_t)completionGroup
{
    return _completionGroup ?: ble_central_manager_dispatch_group();
}

@end




#pragma mark -
static NSString *const kBLERestoreIdentifier = @"kBLERestoreIdentifier";
static NSString *const kBLEConnectedDeviceName = @"kBLEConnectedDeviceName";
static NSString *const kCenterRestoreIdentifier = @"kCenterRestoreIdentifier";


@interface BLECenter () <CBCentralManagerDelegate>

/// 蓝牙中心
@property (nonatomic, strong) CBCentralManager *manager;

/// 蓝牙中心配置
@property (nonatomic, strong) BLECenterConfig *config;
/// 状态
@property (nonatomic, assign) BLECenterState state;

/// 提示打开蓝牙的alert是否正在展示
@property (nonatomic, assign) BOOL powerOffAlertIsPresenting;

/// 要连接的设备名称
@property (nonatomic, copy) NSString *deviceNameNeedConnect;
/// 上一次连接的设备名
@property (nonatomic, copy) NSString *lastConnectedDeviceName;
/// 已连接的设备名
@property (nonatomic, strong) BLEDevice *connectedDevice;

/// 搜索操作
@property (nonatomic, strong) BLEOperation *searchOp;
/// 搜索过滤的block
@property (nonatomic, copy) BOOL (^searchFilterBlock)(NSString *name, NSNumber *rssi);
/// 搜索发现的设备
@property (nonatomic, strong) NSMutableArray<BLEDevice *> *devicesHasDiscover;
/// 次数
@property (nonatomic, assign) NSInteger retrieveCount;

/// 连接操作
@property (nonatomic, strong) BLEOperation *connectOp;

/// 断连操作
@property (nonatomic, strong) BLEOperation *disconnectOp;

/// 会话
@property (nonatomic, strong) BLESession *session;

@end


@implementation BLECenter


#pragma mark Public

#pragma mark 搜索
- (void)searchDevicesWithTimeout:(NSTimeInterval)timeout
                     filterBlock:(BOOL (^)(NSString *, NSNumber *))filterBlock
                        progress:(void (^)(BLEDevice *, NSTimeInterval, BOOL *))progressBlock
                      completion:(void (^)(NSTimeInterval, NSError *))block
{
    [self searchDevicesWithServices:nil
                            options:nil
                            timeout:timeout
                        filterBlock:filterBlock
                           progress:progressBlock
                         completion:block];
}
- (void)searchDevicesWithServices:(NSArray<NSString *>*)UUIDStrings
                          options:(NSDictionary<NSString *,id> *)options
                          timeout:(NSTimeInterval)timeout
                      filterBlock:(BOOL (^)(NSString *, NSNumber *))filterBlock
                         progress:(void (^)(BLEDevice *, NSTimeInterval, BOOL *))progressBlock
                       completion:(void (^)(NSTimeInterval, NSError *))completion
{
    BLELog(@"执行搜索设备");
    if (_searchOp != nil)
    {
        // 如果当前正在执行搜索，直接返回蓝牙忙碌
        !completion ?: completion(kNoneInterval, [NSError bleBusyError]);
        return;
    }
    
    if (_state == BLECenterStateDisable)
    {
        // 蓝牙不可用，直接回调请打开蓝牙
        !completion ?: completion(kNoneInterval, [NSError bleCenterDisableError]);
        [self popPowerOffAlertIfNeededed];
        return;
    }
    
    self.searchFilterBlock = filterBlock;
    
    BLEOperation *op = [[BLEOperation alloc]init];
    op.type = BLEOperationTypeSearch;
    if (timeout > 0)
    {
        op.timeout = timeout;
    }
    
    __weak __typeof__ (self) weakSelf = self;
    
    [op setInvalidateBlock:^(BLEOperation *op, NSTimeInterval use, NSError *error) {
        __strong __typeof__ (weakSelf) self = weakSelf;
        
        if (error)
        {
            if (error.code == BLECodeTimeout &&
                self.devicesHasDiscover.count) {
                error = nil;
            }
        }
        if (op.hasFired)
        {
            [self.manager stopScan];
            self.searchFilterBlock = nil;
            [self.devicesHasDiscover removeAllObjects];
            _devicesHasDiscover = nil;
        }
        self.searchOp = nil;
        !completion ?: completion(use, error);
    }];
    
    __block BOOL stop = NO;
    [op setScheduleBlock:^(BLEOperation *op, NSTimeInterval use, BLEDevice *obj) {
        !progressBlock ?: progressBlock(obj, use, &stop);
        if (stop)
        {
            [op invalidate];
        }
    }];
    
    [op setFireBlock:^(BLEOperation *op, NSTimeInterval use) {
        __strong __typeof__ (weakSelf) self = weakSelf;

        [self retrieveConnectedDevicesRecursively];
        
        NSArray *UUIDs = [UUIDStrings ble_map:^id(NSString *obj, NSUInteger idx) {
            return [CBUUID UUIDWithString:obj];
        }];
        
        BLELog(@"执行搜索");
        [self.manager scanForPeripheralsWithServices:UUIDs options:options];
    }];
    
    self.searchOp = op;
    
    if (_state == BLECenterStateAvailable)
    {
        [op fire];
        return;
    }
    BLELog(@"搜索设备时蓝牙状态未初始化");
    /// 为了解决蓝牙打开的状态却不回调代理事件的系统问题
    [op start];
}

- (void)stopSearch
{
    if (self.searchOp)
    {
        [self.searchOp invalidateWithError:nil];
    }
}

#pragma mark 连接

- (void)connectAutomaticallyWithTimeout:(NSTimeInterval)timeout
                                options:(NSDictionary<NSString *, id> *)options
                             completion:(void (^)(NSTimeInterval, NSError *))completion
{
    NSString *name = self.config.deviceNameNeedConnectAutomatically;
    if (!name.length)
    {
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        name = [defaults objectForKey:kBLEConnectedDeviceName];
    }
    if (!name.length)
    {
        NSError *error = [NSError errorWithCode:BLEDeviceParamInvalid message:@"Not found device name that need connected!"];
        !completion ?: completion(kNoneInterval, error);
        return;
    }
    BLELog(@"开始自动连接，自动连接设备名 %@", name);
    [self connectToDeviceNamed:name
                       timeout:timeout
                       options:options
                    completion:completion];
}
- (void)connectToDeviceNamed:(NSString *)name
                     timeout:(NSTimeInterval)timeout
                     options:(NSDictionary<NSString *, id> *)options
                  completion:(void (^)(NSTimeInterval, NSError *))completion
{
    if (!name.length)
    {
        NSError *error = [NSError errorWithCode:BLEDeviceParamInvalid message:@"The name is invalid!"];
        !completion ?: completion(kNoneInterval, error);
        return;
    }
    BLEDevice *device = nil;
    NSArray *devices = [self retrieveConnectedDevices];
    device = [devices ble_match:^BOOL(BLEDevice *obj) {
        return [obj.name isEqualToString:name];
    }];
    if (!device)
    {
        device = [self.devicesHasDiscover ble_match:^BOOL(BLEDevice *obj) {
            return [obj.name isEqualToString:name];
        }];
    }
    if (device)
    {
        [self connectToDevice:device
                      timeout:timeout
                      options:options
                   completion:completion];
        return;
    }
    else
    {
        __strong __typeof__ (completion) strongCompletion = completion;
        BLELog(@"没有找到设备%@，开始执行搜索", name);
        [self searchDevicesWithServices:nil
                                options:nil
                                timeout:timeout
                            filterBlock:^BOOL(NSString *deviceName, NSNumber *rssi) {
            
            return [deviceName isEqualToString:name];
        } progress:^(BLEDevice *device, NSTimeInterval use, BOOL *stop) {
            
            if ([device.name isEqualToString:name])
            {
                BLELog(@"搜索到%@ 耗时 %lf, 开始连接", name, use);
                [self connectToDevice:device
                              timeout:(timeout - use)
                              options:options
                           completion:^(NSTimeInterval use1, NSError *error) {
                    /// 这里completion 可能会释放变为null
                    !strongCompletion ?: strongCompletion(use + use1, error);
                }];
                *stop = YES;
            }
        } completion:^(NSTimeInterval use, NSError *error) {
            if (error == nil)
            {
                /// 说明搜索成功
                return;
            }
            !strongCompletion ?: strongCompletion(use, error);
        }];
    }
}

- (void)connectToDevice:(BLEDevice *)device
                timeout:(NSTimeInterval)timeout
                options:(NSDictionary<NSString *, id> *)options
             completion:(void (^)(NSTimeInterval, NSError *))completion
{
    BLELog(@"开始执行连接操作");
    if (_connectOp != nil)
    {
        // 如果正在执行连接操作，直接回调蓝牙忙碌
        !completion ?: completion(kNoneInterval, [NSError bleBusyError]);
        return;
    }
    
    /// 已经连接
    if ([self.connectedDevice isEqualToDevice:device] &&
        (device.status & BLEDeviceReachabilityStatusSession))
    {
        BLELog(@"已经连接");
        !completion ?: completion(kNoneInterval, nil);
        return;
    }
    
    if (_state == BLECenterStateDisable)
    {
        // 蓝牙不可用，直接回调请打开蓝牙
        !completion ?: completion(kNoneInterval, [NSError bleCenterDisableError]);
        [self popPowerOffAlertIfNeededed];
        return;
    }
    
    if (self.connectedDevice)
    {
        [self.connectedDevice endOberverState];
    }
    
    self.connectedDevice = nil;
    
    BLEOperation *op = [[BLEOperation alloc]init];
    op.type = BLEOperationTypeConnect;
    if (timeout > 0)
    {
        op.timeout = timeout;
    }
    __strong __typeof__ (completion) strongCompletion = completion;
    
    __weak __typeof__ (self) weakSelf = self;
    [op setInvalidateBlock:^(BLEOperation *op, NSTimeInterval use, NSError *error) {
        __strong __typeof__ (weakSelf) self = weakSelf;
        
        if (error)
        {
            if (op.hasFired)
            {
                [self.connectedDevice endOberverState];
            }
            self.connectedDevice = nil;
            self.connectOp = nil;
            !completion ?: completion(use, error);
        }
        else
        {
            /// 连接成功
            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
            [defaults setObject:device.name forKey:kBLEConnectedDeviceName];
            [defaults synchronize];
            
            self.connectedDevice = device;
            self.connectedDevice.center = self;
            BLELog(@"连接成功，用时%f，准备发现服务特征", use);
            __weak __typeof__ (self) weakSelf = self;
            [device prepareForSessionWithTimeout:timeout-use completion:^(NSTimeInterval use1, NSError *error1) {
                __strong __typeof__ (weakSelf) self = weakSelf;
                !strongCompletion ?: strongCompletion(use+use1, error1);
                self.connectOp = nil;
            }];
        }
    }];
    
    [op setFireBlock:^(BLEOperation *op, NSTimeInterval use) {
        __strong __typeof__ (weakSelf) self = weakSelf;
        [device beginOberverState];
        [self.manager connectPeripheral:device.peripheral
                                options:options];
    }];
    
    self.connectOp = op;
    
    if (_state == BLECenterStateAvailable)
    {
        [op fire];
        return;
    }
    BLELog(@"连接设备时蓝牙状态未初始化");
    /// 为了解决蓝牙打开的状态却不回调代理事件的系统问题
    [op start];
}

#pragma mark 断开连接
- (void)cancelConnectionWithTimeout:(NSTimeInterval)timeout
                         completion:(void (^)(NSTimeInterval, NSError *))completion
{
    BLELog(@"开始执行断开连接操作");
    if (_disconnectOp != nil)
    {
        // 如果正在执行连接操作，直接回调蓝牙忙碌
        !completion ?: completion(kNoneInterval, [NSError bleBusyError]);
        return;
    }
    
    if (!self.connectedDevice)
    {
        self.disconnectOp = nil;
        !completion ?: completion(kNoneInterval, nil);
        return;
    }
    /// 没有连接了，比如关闭蓝牙
    if (self.connectedDevice.state != BLEDeviceStateConnected)
    {
        [self.connectedDevice endOberverState];
        self.connectedDevice = nil;
        self.disconnectOp = nil;
        !completion ?: completion(kNoneInterval, nil);
        return;
    }
    /// 连接的状态，此时蓝牙状态不会是 `BLECenterStateDisable`，但为了保险安全还是加上检测代码
    if (_state == BLECenterStateDisable)
    {
        // 蓝牙不可用，直接回调请打开蓝牙
        !completion ?: completion(kNoneInterval, [NSError bleCenterDisableError]);
        [self popPowerOffAlertIfNeededed];
        return;
    }
    
    BLEOperation *op = [[BLEOperation alloc]init];
    op.type = BLEOperationTypeDisconnect;
    if (timeout > 0)
    {
        op.timeout = timeout;
    }
    
    __weak __typeof__ (self) weakSelf = self;
    [op setInvalidateBlock:^(BLEOperation *op, NSTimeInterval use, NSError *error) {
        __strong __typeof__ (weakSelf) self = weakSelf;
        
        if (op.hasFired)
        {
            
        }
        
        if (!error)
        {
            /// 断开连接成功
            [self.connectedDevice endOberverState];
            self.connectedDevice = nil;
        }
        self.disconnectOp = nil;
        !completion ?: completion(use, error);
    }];
    
    [op setFireBlock:^(BLEOperation *op, NSTimeInterval use) {
        __strong __typeof__ (weakSelf) self = weakSelf;
        [self.manager cancelPeripheralConnection:self.connectedDevice.peripheral];
    }];
    
    self.disconnectOp = op;
    
    if (_state == BLECenterStateAvailable)
    {
        [op fire];
        return;
    }
    
    BLELog(@"连接设备时蓝牙状态未初始化");
    /// 为了解决蓝牙打开的状态却不回调代理事件的系统问题
    [op start];
}

#pragma mark Task

- (BLETask *)sentRequest:(BLERequest *)request
                 success:(void (^)(BLERequest *))success
                 failure:(void (^)(NSError *))failure
{
    BLETask *task = nil;
    
    BLESession *session = self.session;
    
    switch (request.type)
    {
        case BLERequestNormal:
            task = [session normalTaskWithReuest:request];
            [session addNormalTasks:task];
            break;
        case BLERequestMedia:
            task = [session mediaTaskWithReuest:request];
            [session addMediaTasks:task];
            break;
        case BLERequestContact:
            task = [session contactTaskWithReuest:request];
            [session addContactTasks:task];
            break;
    }
    [task setCompletionBlock:^(BLETask *task) {
        if (task.error)
        {
            !failure ?: failure(task.error);
        }
        else
        {
            !success ?: success(task.request);
        }
    }];
    [task resume];
    return task;
}


#pragma mark - BLESessionTaskDelegate

- (void)session:(BLESession *)session
     normalTask:(BLENormalTask *)task
   beginRequest:(BLERequest *)request
{
    [self validateDeviceConnectionWithTimeout:request.timeout completion:^(NSTimeInterval use, NSError *error) {
        if (!error)
        {
            [self.connectedDevice beginNormalTask:task];
        }
        else
        {
            task.error = error;
            task.state = BLETaskStateCompleted;
        }
    }];
}

- (void)session:(BLESession *)session
      mediaTask:(BLEMediaTask *)task
   beginRequest:(BLERequest *)request
{
    [self validateDeviceConnectionWithTimeout:request.timeout completion:^(NSTimeInterval use, NSError *error) {
        if (!error)
        {
            [self.connectedDevice beginMediaTask:task];
        }
        else
        {
            task.error = error;
            task.state = BLETaskStateCompleted;
        }
    }];
}

- (void)session:(BLESession *)session
    contactTask:(BLEContactTask *)task
   beginRequest:(BLERequest *)request
{
    [self validateDeviceConnectionWithTimeout:request.timeout completion:^(NSTimeInterval use, NSError *error) {
        if (!error)
        {
            [self.connectedDevice beginContactTask:task];
        }
        else
        {
            task.error = error;
            task.state = BLETaskStateCompleted;
        }
    }];
}



- (BOOL)validateDeviceConnectionWithTimeout:(NSTimeInterval)timeout
                                 completion:(void (^)(NSTimeInterval use, NSError *error))block
{
    // 1. 检测是不是没有已连接的设备
    if (!self.connectedDevice)
    {
        [self connectAutomaticallyWithTimeout:timeout
                                      options:nil
                                   completion:block];
        return NO;
    }
    
    if (self.connectedDevice.state != BLEDeviceStateConnected)
    {
        [self connectToDevice:self.connectedDevice
                      timeout:timeout
                      options:nil
                   completion:block];
        return NO;
    }
    
    if (!(self.connectedDevice.status & BLEDeviceReachabilityStatusSession))
    {
        [self.connectedDevice prepareForSessionWithTimeout:timeout
                                                completion:block];
        return NO;
    }
    !block ?: block(kNoneInterval, nil);
    return YES;
}

#pragma mark CBCentralManagerDelegate

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    void(^stateUpdateBlock)(BLECenterState) = ^(BLECenterState state) {
        self.state = state;
    };
    
    BLECenterState state = BLECenterStateUnknown;
    CBCentralManagerState centralState = central.state;
    switch (centralState)
    {
        case CBCentralManagerStateUnknown:
            state = BLECenterStateUnknown;
            stateUpdateBlock(state);
            break;
        case CBCentralManagerStateResetting:
            state = BLECenterStateResetting;
            stateUpdateBlock(state);
            break;
        case CBCentralManagerStateUnsupported:
            state = BLECenterStateDisable;
            stateUpdateBlock(state);
            break;
        case CBCentralManagerStateUnauthorized:
            state = BLECenterStateDisable;
            stateUpdateBlock(state);
            break;
        case CBCentralManagerStatePoweredOff:
            state = BLECenterStateDisable;
            stateUpdateBlock(state);
            [self powerOff];
            break;
        case CBCentralManagerStatePoweredOn:
            state = BLECenterStateAvailable;
            stateUpdateBlock(state);
            [self powerOn];
            break;
        default:
            break;
    }
    
    [[NSNotificationCenter defaultCenter]postNotificationName:BLECenterStateDidChangeNotification object:self userInfo:@{@"CBCentralManagerState":@(centralState)}];
}
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *,id> *)dict
{

}
- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary<NSString *,id> *)advertisementData
                  RSSI:(NSNumber *)RSSI
{
    NSString *name = advertisementData[@"kCBAdvDataLocalName"];
    if (![name isKindOfClass:[NSString class]] || !name.length)
    {
        name = peripheral.name;
    }
    if (_searchOp != nil)
    {
        if (!name.length)
        {
            return;
        }
        /// 当前为搜索操作
        BOOL flag = YES;
        if (self.searchFilterBlock)
        {
            flag = self.searchFilterBlock(name, RSSI);
        }
        if (flag)
        {
            BLEDevice *device = [[BLEDevice alloc]initWithPeripheral:peripheral RSSI:RSSI advertisementData:advertisementData];
            if (![self.devicesHasDiscover containsObject:device])
            {
                [self.devicesHasDiscover addObject:device];
                [self.searchOp scheduleWithObject:device];
            }
        }
    }
}
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    [self.connectOp invalidate];
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    [self.connectOp invalidateWithError:error];
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    if (_disconnectOp != nil)
    {
        [self.disconnectOp invalidateWithError:error];
    }
    else
    {
        NSString *name = peripheral.name;
        if ([name isEqualToString:self.connectedDevice.name])
        {
            if (!self.config.connectAutomatically)
            {
                return;
            }
            BLELog(@"%@断开链接，尝试重联", name);
            [self connectToDeviceNamed:name
                               timeout:10
                               options:nil
                            completion:^(NSTimeInterval use, NSError *error) {
                                BLELog(@"连接设备 %@，用时%lf，错误：%@", name, use, error);
                            }];
        }
    }
}

#pragma mark Private

- (void)powerOff
{
    BLELog(@"蓝牙状态关闭");
    NSError *error = [NSError bleCenterDisableError];
    
    if (self.connectOp)
    {
        [self.connectOp invalidateWithError:error];
    }
    if (self.searchOp)
    {
        [self.searchOp invalidateWithError:error];
    }
    if (self.disconnectOp)
    {
        [self.disconnectOp invalidateWithError:nil];
    }
    
    [self popPowerOffAlertIfNeededed];
}



- (void)powerOn
{
    BLELog(@"蓝牙状态打开");
    if (self.connectOp)
    {
        [self.connectOp fire];
    }
    if (self.searchOp)
    {
        [self.searchOp fire];
    }
    if (self.disconnectOp)
    {
        [self.disconnectOp fire];
    }
    
    if (!self.connectOp)
    {
        if (!self.config.connectAutomatically)
        {
            return;
        }
        BLEDevice *device = self.connectedDevice;
        if (!device ||
            !(device.state & BLEDeviceReachabilityStatusSession))
        {
            [self connectAutomaticallyWithTimeout:10 options:nil completion:^(NSTimeInterval use, NSError *error) {
                BLELog(@"自动连接 用时%lf %@", use, error);
            }];
        }
    }
}



- (void)retrieveConnectedDevicesRecursively
{
    if (!self.searchOp)
    {
        /// 没有搜索操作，说明已经搜索到要连接的设备了
        return;
    }
    NSArray *devices = [self retrieveConnectedDevices];
    BLELog(@"第%ld次获取已连接的设备%@", self.retrieveCount, [devices valueForKeyPath:@"name"]);
    if (devices.count)
    {
        self.retrieveCount = 0;
        
        BOOL flag = YES;
        BOOL stop = NO;
        for (BLEDevice *device in devices)
        {
            if (self.searchFilterBlock)
            {
                flag = self.searchFilterBlock(device.name, device.RSSI);
            }
            if (flag)
            {
                if (![self.devicesHasDiscover containsObject:device])
                {
                    [self.devicesHasDiscover addObject:device];
                    [self.searchOp scheduleWithObject:device];
                }
            }
        }
        return;
    }
    self.retrieveCount++;
    /// 尝试每隔1s获取一次，共5次，如果还没有已连接的设备，就是没有连接的设备了
    if (self.retrieveCount > 5)
    {
        return;
    }
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), self.config.completionQueue, ^{
        [self retrieveConnectedDevicesRecursively];
    });
}

- (NSArray<BLEDevice *>*)retrieveConnectedDevices
{
    NSString *mainServerUUIDString = self.config.main.serviceName;
    NSArray *devices = [self retrieveConnectedDevicesWithUUIDStrings:@[mainServerUUIDString]];
    return devices;
}

- (NSArray<BLEDevice *>*)retrieveConnectedDevicesWithUUIDStrings:(NSArray<NSString *>*)UUIDStrings
{
    /// 先关闭蓝牙再立即打开蓝牙调用此方法，返回为空，经测试需要2s秒后才能获取到
    NSArray *peripherals = [self.manager retrieveConnectedPeripheralsWithServices:[UUIDStrings ble_map:^id(NSString *obj, NSUInteger idx) {
        return [CBUUID UUIDWithString:obj];
    }]];
    return [peripherals ble_map:^BLEDevice *(CBPeripheral *obj, NSUInteger idx) {
        BLEDevice *device = [[BLEDevice alloc]initWithPeripheral:obj RSSI:@(55 - idx) advertisementData:nil];
        device.retrieveFromCenter = YES;
        return device;
    }];
}

- (void)popPowerOffAlertIfNeededed
{
    if (!self.config.popAlertWhenPowerOff ||
        self.powerOffAlertIsPresenting)
    {
        return;
    }
    
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
    UIAlertView *alertView = [[UIAlertView alloc]init];
    alertView.delegate = self;
    alertView.title = @"Bluetooth is off. Please turn on your Bluetooth via settings.";
    [alertView addButtonWithTitle:@"OK"];
    [alertView show];
    _powerOffAlertIsPresenting = YES;
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    _powerOffAlertIsPresenting = NO;
}

#pragma mark Setter && Getter

- (NSMutableArray<BLEDevice *> *)devicesHasDiscover
{
    if (_devicesHasDiscover) return _devicesHasDiscover;
    _devicesHasDiscover = [NSMutableArray array];
    return _devicesHasDiscover;
}

- (BLESession *)session
{
    if (_session) return _session;
    _session = [[BLESession alloc]initWithDelegate:self];
    return _session;
}

#pragma mark Liftcyle

+ (instancetype)center
{
    return [[[self class]alloc]init];
}

- (instancetype)init
{
    return [self initWithConfig:nil];
}

- (instancetype)initWithConfig:(BLECenterConfig *)config
{
    self = [super init];
    if (!self) return nil;
    
    if (!config)
    {
        config = [BLECenterConfig defaultConfig];
    }
    
    _enable = YES;
    
    _config = config;
    
    BOOL showPowerAlert = YES;
    if (config.popAlertWhenPowerOff)
    {
        showPowerAlert = NO;
    }
    dispatch_queue_t queue = config.completionQueue;
//    dispatch_queue_t queue = nil;
    
    NSArray *backgroundModes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIBackgroundModes"];
    if ([backgroundModes containsObject:@"bluetooth-central"])
    {
        NSDictionary *options = @{
                                  CBCentralManagerOptionShowPowerAlertKey
                                  : @(showPowerAlert),
                                  CBCentralManagerOptionRestoreIdentifierKey
                                  : kCenterRestoreIdentifier
                                  };
        //后台模式
        _manager = [[CBCentralManager alloc] initWithDelegate:self queue:queue options:options];
    }
    else
    {
        //非后台模式
        _manager = [[CBCentralManager alloc] initWithDelegate:self queue:queue];
    }
    
    return self;
}

- (BOOL)respondsToSelector:(SEL)aSelector
{
    if (aSelector == @selector(centralManager:willRestoreState:))
    {
        NSArray *backgroundModes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UIBackgroundModes"];
        return [backgroundModes containsObject:@"bluetooth-central"];
    }
    return [[self class] instancesRespondToSelector:aSelector];
}

- (void)setLogEnable:(BOOL)enable
{
    kBLELogEnable = enable;
}

@end




